-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Display number of unread messages as a badge on the jump-to-bottom button. Send fully-read receipts more efficiently. #206
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comments about the Jump to Top (JTT) button -- we're not yet ready to properly implement that, so I think it should be removed, or at least hidden until we can fix it.
Everything else looks fine, thanks!
Currently there's a bug in the portallist's is_at_end function which returns true even when the scroll is not at the bottom. Hence the JTB will not show. |
I've fixed this as of last week; I rewrote the entire |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, the code is looking better and is certainly a lot more efficient and easier to understand!
It's good that you're querying the user's latest fully read state when you show the room timeline for the first time. However, that approach will only catch changes to the user's read receipt once, when the room is first shown. Imagine the following scenario:
- The user opens a room in Robrix
- Robrix is open, but sitting idle on their computer
- The user opens another client, e.g., on their phone
- New messages arrive for that room
- The user sees those new messages and interacts with them on their phone
Then, when the user returns to Robrix, the latest fully read receipt for that room will be wrong/outdated, because it didn't get updated while the user was interacting with their phone.
I think we actually need to subscribe to this state change such that we can know if it has changed on other devices. (We can do this in a future PR, but if it's easy then we should just do it now.)
src/shared/jump_to_bottom_button.rs
Outdated
if unread_message_count > 0 { | ||
self.visible = true; | ||
self.view(id!(unread_message_badge)).set_visible(true); | ||
self.label(id!(unread_messages_count)).set_text(&format!("{}", unread_message_count)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably limit it to a certain number of events such that the label doesn't look ridiculous if there are 10000000+ unread events.
We could do something like up to two digits (a max value of 99) or three digits (a max value of 999) until we just show 99+
or 999+
. Depends on how wide the label can be while still looking good.
Use your best judgment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, looks pretty good. The circular background might need to be an oval/ellipse, since we need more padding on the left and right of the 99+
string to make it look more professional and readable.
I think if you set the width
of the parent view of the label to be Fit
, with a small amount of left & right padding, that should do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Co-authored-by: Kevin Boos <[email protected]>
Co-authored-by: Kevin Boos <[email protected]>
…to send_read+jtb
Added subscribe_to_updates mechanism to update the latest read receipt in multiple devices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think this looks pretty good. I left a few small comments, but I have one major request: let's just remove the concept of the 5-second timer. It's too complicated and will probably just confuse users. Let's just send the read receipt based on the user's scrolling actions, which should also make the code simpler and more efficient.
I think removing the timer is also consistent with what power users expect. For example, sometimes I just want to quickly skim the latest messages from 10 different rooms and consider those as fully read. That process should only take me a few seconds, not over 50 seconds (5 seconds for each room).
Also, I still don't think that storing the latest read receipt event in the ALL_ROOM_INFO
map is the correct way to do things. It is inefficient, requires undesirable locking, and it doesn't even seem to be necessary.
Thus, let's consider how we could improve this. It looks like that info is only used within the RoomScreen, and it is a form of per-room data. Since we're already sending TimelineUpdates to the RoomScreen widget from the backend update subscriber, why wouldn't we just keep that information directly within each room's UI state? That way, we avoid locking, we avoid the overhead of adding and updating that info within the ALL_ROOM_INFO
hashmap, and we avoid the memory usage of storing it in multiple different places.
What do you think about that approach? If you like it, please make the changes to follow that design instead.
/// Timer to send fully read receipt | ||
/// | ||
/// 5 secs timer starts when user scrolled down and scrolled_past_read_marker is true | ||
/// Last displayed event will be sent as fully read receipt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pro-tip: doc comments like this get rendered into Html, so you need to format them as full sentences rather than just sentence fragments across line breaks.
/// Timer to send fully read receipt | |
/// | |
/// 5 secs timer starts when user scrolled down and scrolled_past_read_marker is true | |
/// Last displayed event will be sent as fully read receipt | |
/// Timer used to send fully-read receipts for the current room. | |
/// | |
/// We start a short (5 second) timer when the user scrolls down, | |
/// if `scrolled_past_read_marker` is true. Then, we send a fully-read receipt | |
/// for the last displayed event in this room. |
*moved_to_queue = true; | ||
} | ||
// Send fully read receipt for last displayed event 5 Seconds after user scrolled past the read marker | ||
if self.fully_read_timer.is_event(event).is_some() && !self.fully_read_timer.is_empty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain (and add a comment) why it's necessary to check that the fully_read_timer
is not empty?
Also, what does it mean for a timer to be non-empty?
} | ||
if let Some(unread_messages_count) = unread_messages_count { | ||
jump_to_bottom.show_unread_message_badge(cx, unread_messages_count); | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why continue
here? that seems odd, as we typically wouldn't want to skip anything else beneath this point.
height: Fit, | ||
padding: { left: 0.0, right: 2.0 } | ||
text: "", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this unread messages label is not centered horizontally with respect to the jump-to-bottom button. You can try align: {x: 0.5}
here, but I think you'll also need more than that change to make it perfectly centered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The right-side padding of 2.0
will also make things look off-center too.
@@ -287,6 +288,14 @@ pub enum MatrixRequest { | |||
/// * If `false` (recommended), details will be fetched from the server. | |||
local_only: bool, | |||
}, | |||
/// Request to fetch the fully read event in the given room's timeline. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Request to fetch the fully read event in the given room's timeline. | |
/// Request to fetch the user's latest fully-read event for the given room. |
if let Some(latest_active) = serde_json::to_value(room.read_receipts()) | ||
.unwrap_or_default() | ||
.get("latest_active") | ||
.unwrap_or(&serde_json::Value::Null) | ||
.get("event_id") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is unreadable without proper formatting
if let Some(latest_active) = serde_json::to_value(room.read_receipts()) | |
.unwrap_or_default() | |
.get("latest_active") | |
.unwrap_or(&serde_json::Value::Null) | |
.get("event_id") { | |
if let Some(latest_active) = serde_json::to_value(room.read_receipts()) | |
.unwrap_or_default() | |
.get("latest_active") | |
.unwrap_or(&serde_json::Value::Null) | |
.get("event_id") | |
{ |
Ok(sent) => log!("{} send fully read receipt to room {room_id} for event {event_id}", | ||
if sent { "Sent" } else { "Already sent" } | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok(sent) => log!("{} send fully read receipt to room {room_id} for event {event_id}", | |
if sent { "Sent" } else { "Already sent" } | |
), | |
Ok(sent) => log!("{} fully read receipt to room {room_id} for event {event_id}", | |
if sent { "Sent" } else { "Already sent" } | |
), |
@@ -884,8 +1015,12 @@ struct RoomInfo { | |||
timeline_subscriber_handler_task: JoinHandle<()>, | |||
/// A drop guard for the event handler that represents a subscription to typing notices for this room. | |||
typing_notice_subscriber: Option<EventHandlerDropGuard>, | |||
/// A broadcast receiver for room updates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incorrect comment, this is not a receiver, it is boolean.
@@ -1877,6 +1892,9 @@ impl RoomScreen { | |||
} | |||
); | |||
|
|||
// Query for User's fully read event | |||
submit_async_request(MatrixRequest::GetFullyReadEvent { room_id: room_id.clone() }); | |||
submit_async_request(MatrixRequest::SubscribeToUpdates { room_id: room_id.clone(), subscribe: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and do you ever unsubscribe from these updates?
// Using Serde_json to obtain the latest_active event_id | ||
// Room's read receipt's latest_active event is not public, but the ReadReceipt struct | ||
// is serializable | ||
if let Some(latest_active) = serde_json::to_value(room.read_receipts()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
surely there is an easier way to accomplish this?
Check out the docs for both Room
and Timeline
-- there are several methods related to getting the latest read receipt for a given user.
For example, here's one to get you started: https://matrix-org.github.io/matrix-rust-sdk/matrix_sdk_ui/timeline/struct.Timeline.html#method.latest_user_read_receipt
Fixed #152 (review)
Fixed #131
Added Jump to Top view
Added Jump to bottom badge counter
scrollup2.mp4